using System;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

using Microsoft.AspNetCore.Http;
using Microsoft.Net.Http.Headers;

namespace Esst.Extensions.Middlewares
{
    /// <summary> 平台中间件 </summary>
    public class HttpMiddleware
    {
        private readonly RequestDelegate _next;

        public HttpMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            
                if (context.Request.Path.Value == "/api")
                    await context.Response.WriteAsync("Domain:Runing...");
                else
                    await InvokeMain(context);
           
        }

        public async Task InvokeMain(HttpContext context)
        {
            // 只处理标记为NetTrmpAPI的接口数据
            var endpoint = context.GetEndpoint();
            var actionLogAttribute = endpoint?.Metadata?.GetMetadata<HttpApiAttribute>();
            if (actionLogAttribute == null)
            {
                await _next.Invoke(context);
                return;
            }

            var request = context.Request;
            if (!"POST".Equals(request.Method, StringComparison.OrdinalIgnoreCase) || !request.ContentLength.HasValue || request.ContentLength.Value <= 0)
            {
                await _next.Invoke(context);
                return;
            }
            var response = context.Response;

            // 替换响应流用来对相应进行加密
            var oriResponseStream = response.Body;
            using var replaceStream = new MemoryStream();
            response.Body = replaceStream;

            var token = "aa";//GetToken(context);
            var aes =  CreatAes(token);
            // 前端是否启用加密传输
            var encryptionEnable = true;

            string realResponseValue = null;
            // 重写响应类型
            response.OnStarting(async () =>
            {
                response.ContentLength = realResponseValue is null ? null : Encoding.UTF8.GetByteCount(realResponseValue);
                if (encryptionEnable)
                    response.ContentType = "text/plain";
            });

            // 请求值
            string requestValue = null;

            #region 请求替换解密

            using (var reader = new StreamReader(context.Request.Body, Encoding.UTF8, true))
                requestValue = await reader.ReadToEndAsync().ConfigureAwait(false);

#if DEBUG
            encryptionEnable = string.IsNullOrWhiteSpace(requestValue) || requestValue[0] != '{';
#endif

            if (encryptionEnable)
            {
                requestValue = await Decrypt(aes, requestValue).ConfigureAwait(false);
            }

            var requestContent = new StringContent(requestValue, Encoding.UTF8, "application/json");

            request.ContentType = requestContent.Headers.ContentType.ToString();
            request.ContentLength = requestContent.Headers.ContentLength;
            request.Body = await requestContent.ReadAsStreamAsync();

            #endregion 请求替换解密

            var startTime = DateTime.Now.Ticks;
            await _next.Invoke(context);
            var endTime = DateTime.Now.Ticks;

            #region 响应加密

            string oriResponseValue = null;
            if (context.Response.BodyWriter is not null)
                await context.Response.BodyWriter.FlushAsync();
            await replaceStream.FlushAsync();
            using (var stream = new StreamReader(replaceStream, Encoding.UTF8, true))
            {
                replaceStream.Seek(0, SeekOrigin.Begin);
                oriResponseValue = await stream.ReadToEndAsync().ConfigureAwait(false);
            }

            realResponseValue = oriResponseValue;
            if (encryptionEnable && !string.IsNullOrWhiteSpace(oriResponseValue))
            {
                realResponseValue = await Encrypt(aes, oriResponseValue).ConfigureAwait(false);
            }

            response.Body = oriResponseStream;

            // 发送请求记录
          //  SendRecord(context, requestValue, oriResponseValue, startTime, endTime);

            await response.WriteAsync(realResponseValue);

            #endregion 响应加密
        }

        /// <summary> 发送请求记录 </summary>
        /// <param name="context"> </param>
        /// <param name="requestValue"> </param>
        /// <param name="oriResponseValue"> </param>
        /// <param name="startTime"> </param>
        /// <param name="endTime"> </param>
        //private static void SendRecord(HttpContext context, string requestValue, string oriResponseValue, long startTime, long endTime)
        //{
        //    var user = Thread.CurrentPrincipal as ESPrincipal;

        //    var duration = (endTime - startTime) / 10000;

        //    var path = context.Request.Path;
        //    var statusCode = context.Response.StatusCode;
        //    var model = new Logs.RequestRecord.Model.UrlRequestMQData()
        //    {
        //        PlatformType = (int)NetTrmpEnvironment.PlatformType,
        //        Path = path,
        //        HttpCode = statusCode,
        //        UserId = user?.UserIdentity?.UserId,
        //        Time = startTime,
        //        Client = user?.ClientInfo,
        //        Request = requestValue,
        //        Response = oriResponseValue,
        //        Duration = (duration > int.MaxValue || duration < 0) ? -1 : Convert.ToInt32(duration)
        //    };

        //    _ = Logs.RequestRecord.BLL.Recorder.LogRequest(model);
        //}

        //private static string GetToken(HttpContext context)
        //{
        //    if (!context.Request.Headers.TryGetValue(HeaderNames.Authorization, out var value))
        //        return null;

        //    if (!AuthenticationHeaderValue.TryParse(value, out var auth))
        //        return null;

        //    if (auth.Scheme != ESAuthFilter.AuthScheme)
        //        return null;

        //    return auth.Parameter;
        //}

        private static Aes CreatAes(string token)
        {
            var time = DateTime.UtcNow;

            var key = (token?.ToLower() ?? "Esst") + time.Year + time.Month + time.Day;

            var keyHash = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(key));
            var iv = MD5.Create().ComputeHash(keyHash);

            var aes = Aes.Create();
            aes.Key = keyHash;
            aes.IV = iv;
            aes.Mode = CipherMode.ECB;
            aes.Padding = PaddingMode.PKCS7;

            return aes;
        }

        private static async Task<string> Encrypt(Aes aes, string value)
        {
            var decrypt = aes.CreateEncryptor();

            using (var msEncrypt = new MemoryStream())
            {
                using (var csEncrypt = new CryptoStream(msEncrypt, decrypt, CryptoStreamMode.Write))
                {
                    using (var swEncrypt = new StreamWriter(csEncrypt, new UTF8Encoding(false)))
                    {
                        await swEncrypt.WriteAsync(value).ConfigureAwait(false);
                        await swEncrypt.FlushAsync().ConfigureAwait(false);
                    }
                    await csEncrypt.FlushAsync().ConfigureAwait(false);
                }

                await msEncrypt.FlushAsync().ConfigureAwait(false);
                var encrypted = msEncrypt.ToArray();
                return Convert.ToHexString(encrypted);
            }
        }

        private static async Task<string> Decrypt(Aes aes, string value)
        {
            var decrypt = aes.CreateDecryptor();
            var data = Convert.FromHexString(value);

            using (var msDecrypt = new MemoryStream(data))
            {
                using (var csDecrypt = new CryptoStream(msDecrypt, decrypt, CryptoStreamMode.Read))
                {
                    using (var srDecrypt = new StreamReader(csDecrypt, Encoding.UTF8))
                    {
                        return await srDecrypt.ReadToEndAsync().ConfigureAwait(false);
                    }
                }
            }
        }
    }
}